package GA.tournaments;

import java.util.Random;

import LUDOSimulator.LUDOPlayer;

public class Ga3LUDOPlayer implements LUDOPlayer {

	LUDOBoard board;
	Random rand;
	int[][] current_board;
	int[][] new_board;
	int[][] new_board2;
	float max;
	int bestIndex;
	int[] w;
	int[] actionList = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
	int[] defaultList = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
	int[] weightList = new int[actionList.length];
	int myColor; // Color of player
	boolean hitMeHome = false;
	boolean actionFound = false;
	int action;// = 1;
	float aValue;
	int j = 0;

	public Ga3LUDOPlayer(LUDOBoard board)
	{
		this.board = board;
		rand = new Random();
		myColor = board.getMyColor();
	}

	// Used for training.
	public void updateGa(int[] w) {
		this.w = w;
		actionList = defaultList.clone(); // reset list
		fillActionList();
	}

	private void fillActionList(){
		// Descending selection sort by weight.
		weightList = w.clone();
		for(int i = 0; i < weightList.length-1; i++) {
			for(int j = i+1; j<weightList.length; j++) {
				if(weightList[i] < weightList[j]) { // ">" for ascending sort.
					int temp = weightList[i];
					weightList[i] = weightList[j];
					weightList[j] = temp;	
					int temp2 = actionList[i];
					actionList[i] = actionList[j];
					actionList[j] = temp2;
				}
			}
		}		
		// End sort.
	}

	@Override
	public void play() {
		board.print("Genetic player 3 playing");

		board.rollDice();
		max =-1;
		bestIndex = -1;
		for(int i=0;i<4;i++) // Check all bricks to find the one with the highest weight to move.
		{
			float value = analyzeBrickSituation(i); 
			if((value>max) && (value>0)) {
				bestIndex = i;
				max = value;
			}
		}
		if(bestIndex!=-1) board.moveBrick(bestIndex);
	}

	// Evaluate which brick to move. For each brick find use the "weights" in descending order 
	// to find the action. The rand.nextFloat() is used, so in case more bricks can perform the 
	// same action, then a random is picked instead of the first brick. 
	public float analyzeBrickSituation(int i) {
		if(board.moveable(i)) {
			current_board = board.getBoardState();
			new_board = board.getNewBoardState(i, myColor, board.getDice());

			aValue = (float) 4.0; // Default value if a brick can be moved.
			actionFound = false;
			hitMeHome = hitMySelfHome(current_board, new_board); // Check if move will hit brick home. This is done here as it is used in 5 of the cases.
			j = 0;

			while(!actionFound && (j < w.length) ) {				
				action = actionList[j] ;
				switch(action) {			
				case 1: 
					if(hitOpponentHome(current_board,new_board)) {
						actionFound = true;
						aValue = weightList[j]+rand.nextFloat();
					}
					break;
				case 2 :
					if(!hitMeHome) { // If field is a star, check that field is not occupied by two brick of another color.
						if(board.isStar(new_board[myColor][i])) {
							actionFound = true;
							aValue = weightList[j]+rand.nextFloat();
						}
					}
					break;
				case 3 :
					if(moveOut(current_board,new_board)) {
						actionFound = true;
						aValue = weightList[j]+rand.nextFloat(); //(float)0.1; // should weight be used?
					}
					break;
				case 4 :
					if(board.atHome(new_board[myColor][i],myColor)) {
						actionFound = true;
						aValue = weightList[j]+rand.nextFloat();
					}
					break;
				case 5 : // Default move if possible, without hitting the brick home. This case could be used for a "catch all" option.
					//if(board.atField(new_board[myColor][i])) {
					if(!hitMeHome) {
						actionFound = true;
						aValue = weightList[j]+rand.nextFloat();
					}
					//}
					break;	
				case 6:
					if(hitMeHome) {
						actionFound = true;
						aValue = weightList[j]+rand.nextFloat();
					}
					break;
				case 7:
					if(board.almostHome(new_board[myColor][i],myColor)) {
						if(board.almostHome(current_board[myColor][i],myColor)) {
							actionFound = true;
							aValue = weightList[j]+rand.nextFloat();
						}
					}
					break;
				case 8: // Check if next location is a globe, but not a starting globe. Also check that it is not occupied by another player.
					if(!hitMeHome) {
						if(gotoGlobe(current_board, new_board, i)) {
							actionFound = true;
							aValue = weightList[j]+rand.nextFloat();
						}
					}
					break;
				case 9: // Check if next location is a globe which is a starting location for another color. Also check that it is not occupied by another player.
					if(!hitMeHome) {
						if(gotoStartGlobe(current_board, new_board, i)) {
							actionFound = true;
							aValue = weightList[j]+rand.nextFloat();
						}
					}
					break;
				case 10: // Check if the brick can move to a position with another brick of same color.
					if(ownBrick(i)) {
						actionFound = true;
						aValue = weightList[j]+rand.nextFloat();						
					}
					break;
				case 11: // Hit opponent in next round.
					if(!hitMeHome) {
						if(hitOppNext(new_board, i) ) {
							actionFound = true;
							aValue = weightList[j]+rand.nextFloat();						
						}
					}
					break;
				case 12: // Avoid being hit by opponent in next round.
					if(!hitMeHome) {
						if(hitMyselfNext(new_board, i) ) {
							actionFound = true;
							aValue = weightList[j]+rand.nextFloat();						
						}
					}
					break;
				case 13: // Move front brick (in "open field").
					if(!hitMeHome) {
						if(isFrontBrick(i)) {
							actionFound = true;
							aValue = weightList[j]+rand.nextFloat();						
						}
					}
					break;
				case 14: // Move last brick (in "open field").
					if(!hitMeHome) {
						if(isLastBrick(i)) {
							actionFound = true;
							aValue = weightList[j]+rand.nextFloat();						
						}
					}
					break;

				default :
					aValue = 1+rand.nextFloat(); // This should not be used unless there is an index error.
				}
				j++;
			}
			return aValue;
		}
		else {
			return 0;
		}
	}

	private boolean moveOut(int[][] current_board, int[][] new_board) {
		for(int i=0;i<4;i++) {
			if(board.inStartArea(current_board[myColor][i],myColor)&&!board.inStartArea(new_board[myColor][i],myColor)) {
				return true;
			}
		}
		return false;
	}

	// Check if one of the other player's brick are moved home after next move.
	private boolean hitOpponentHome(int[][] current_board, int[][] new_board) {
		for(int i=0;i<4;i++) {
			for(int j=0;j<4;j++) {
				if(myColor!=i) {
					if(board.atField(current_board[i][j])&&!board.atField(new_board[i][j])) {
						return true;
					}
				}
			}
		}
		return false;
	}

	// Compare if the the same brick are in the starting area before and after the move are different (one is hit home) and return true if so.
	private boolean hitMySelfHome(int[][] current_board, int[][] new_board) {
		for(int i=0;i<4;i++) {
			if(!board.inStartArea(current_board[myColor][i],myColor)&&board.inStartArea(new_board[myColor][i],myColor)) {
				return true;
			}
		}
		return false;
	}

	// Check if next location is a globe, but not a starting globe. Also check that it is not occupied by another player.
	public boolean gotoGlobe(int[][] current_board, int[][] new_board, int i) {
		int[][] bricks_state2 = board.getNewBoardState(i, myColor, board.getDice());
		int location2 = bricks_state2[myColor][i];
		if( (location2 == 8) || (location2 == 21) || (location2 == 34) || (location2 == 47) ) {
			// Check if globe is free, by checking if the brick will not be at home in next round.
			//if(!hitMySelfHome(current_board, new_board)) - done in case
			return true;
		}
		return false;
	}

	// Check if next location is a starting globe. Also check that it is not occupied by another player -done in case.
	public boolean gotoStartGlobe(int[][] current_board, int[][] new_board, int i) {
		// Check if globe is own starting globe.
		if(moveOut(current_board,new_board))
			return true;
		// Else check if brick is moving to one of the starting globes.
		int[][] bricks_state2 = board.getNewBoardState(i, myColor, board.getDice());
		int location2 = bricks_state2[myColor][i];
		if( ((location2 == 0) || (location2 == 13) || (location2 == 26) || (location2 == 39)) && (location2 != myColor*13) ) { // (loaction2 == 00) &&  for yellow
			return true;
		}
		return false;
	}


	// Check if the brick can move to a position with another brick of same color.
	public boolean ownBrick(int b) {
		int[] pos = new int[4];
		pos = board.getMyBricks();
		for(int i=0;i<4;i++) {
			if((b!=i) && ((pos[b]+board.getDice())==pos[i]) ) // See if next position matches other than itself.
				return true;
		}		
		return false;
	}	

	// Check if the brick can hit an opponent in next round(assuming it does not move)
	public boolean hitOppNext(int[][] new_board, int i) {
		// For each possible roll, check if opponent is hit home.
		for(int d = 1; d < 7; d++) {
			new_board2 = board.getNewBoardState(i, myColor, d);
			if(hitOpponentHome(new_board, new_board2)) {
				return true;
			}
		}		
		return false;
	}

	// Check if the brick can hit an opponent in next round(assuming it does not move)
	public boolean hitMyselfNext(int[][] new_board, int i) {
		// For each opponent.
		for(int p = 0; p < 4; p++) {
			if(p != myColor)
				// For each possible roll, check if the opponent can hit me home.
				for(int d = 1; d < 7; d++) {
					new_board2 = board.getNewBoardState(i, p, d);
					if(hitMySelfHome(new_board, new_board2)) {
						return true;
					}
				}		
		}
		return false;
	}

	public boolean isFrontBrick(int b) {
		int[] pos = new int[4];
		int offset = myColor*13;
		pos = board.getMyBricks();

		int pb = pos[b];
		int pi;
		for(int i=0;i<4;i++) {
			pi = pos[i];
			// Apply offset for other colors
			if(pb < offset)
				pb += 52;
			if(pi < offset)
				pi += 52;

			if((b!=i) && (pi < 99) && (pb < pi) ) // For other than itself, bricks in field -check if it is behind any other bricks.
				return false;	
		}
		return true;
	}

	public boolean isLastBrick(int b) {
		int[] pos = new int[4];
		int offset = myColor*13;
		pos = board.getMyBricks();

		// Short version
		int pb = pos[b];
		int pi;
		for(int i=0;i<4;i++) {
			pi = pos[i];
			// Apply offset for other colors
			if(pb < offset)
				pb += 52;
			if(pi < offset)
				pi += 52;

			if((b!=i) && (pi < 99) && (pb > pi) ) // For other than itself, bricks in field -check if it is behind any other bricks.
				return false;	
		}
		return true;
	}
}
